import time
import asyncio
import logging
import os
from datetime import datetime
from playwright.async_api import async_playwright
from ncatbot.core import BotClient
from ncatbot.utils import config

# 创建日志目录
log_dir = "logs"
if not os.path.exists(log_dir):
    os.makedirs(log_dir)

# 配置程序日志
program_logger = logging.getLogger('program_logger')
program_logger.setLevel(logging.DEBUG)

# 清除已有的处理器
if program_logger.handlers:
    program_logger.handlers.clear()

# 创建程序日志处理器
program_log_handler = logging.FileHandler(os.path.join(log_dir, 'program.log'), encoding='utf-8')
program_log_handler.setLevel(logging.DEBUG)
program_log_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
program_log_handler.setFormatter(program_log_formatter)
program_logger.addHandler(program_log_handler)

# 添加控制台输出
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_handler.setFormatter(program_log_formatter)
program_logger.addHandler(console_handler)

# 配置消息日志
message_logger = logging.getLogger('message_logger')
message_logger.setLevel(logging.INFO)

# 清除已有的处理器
if message_logger.handlers:
    message_logger.handlers.clear()

# 创建消息日志处理器 - 使用追加模式，避免覆盖历史记录
message_log_handler = logging.FileHandler(os.path.join(log_dir, 'messages.log'), mode='a', encoding='utf-8')
message_log_handler.setLevel(logging.INFO)
message_log_formatter = logging.Formatter('%(asctime)s - %(message)s')
message_log_handler.setFormatter(message_log_formatter)
message_logger.addHandler(message_log_handler)

# 定义用户数据目录，用于保存登录状态
USER_DATA_DIR = "./browser_data"

async def main():
    # 初始化QQ机器人
    bot = BotClient()
    api = bot.run_backend(debug=True)  # 后台线程启动，返回全局 API（同步友好）

    notify_qq = 1234  # 填自己要被通知的qq号

    # 发送测试消息，确认机器人启动成功
    api.send_private_text_sync(notify_qq, "抖音消息监听器已启动，开始监听新消息...")
    
    async with async_playwright() as p:
        # 使用Chrome浏览器，保留用户数据以保存登录状态
        browser = await p.chromium.launch_persistent_context(
            user_data_dir=USER_DATA_DIR,
            headless=False,
            args=[
                "--start-maximized",  # 最大化窗口
            ],
            ignore_https_errors=True  # 忽略HTTPS错误
        )
        
        page = await browser.new_page()
        
        # 打开抖音精选页面，增加超时时间并使用更宽松的等待条件
        await page.goto("https://www.douyin.com/jingxuan", timeout=120000, wait_until="domcontentloaded")
        
        # 等待页面基本DOM加载完成（不等待网络空闲状态，避免超时）
        # 抖音网站可能有持续的网络请求，使用简单的超时等待确保页面加载稳定
        await page.wait_for_timeout(5000)
        
        # 等待60秒，让用户有时间手动登录（如果需要）
        program_logger.info("等待登录...请在60秒内完成登录操作")
        # await page.wait_for_timeout(60000)
        
        # 登录后保存状态并继续
        program_logger.info("登录完成，开始监听消息...")
        
        # 定义小红点元素选择器
        NOTIFICATION_DOT_SELECTOR = "div.xQFyrmH1"
        # 定义通知展开栏中的小红点元素选择器
        MESSAGE_DOT_SELECTOR = "div.LF_j34HE"
        # 定义通知展开栏选择器（从用户提供的DOM中提取）
        NOTIFICATION_PANEL_SELECTOR = "div.pMlWgk97.jO21V8V_.G7E2uXlP"
        # 定义通知图标选择器（备选方案）
        NOTIFICATION_ICON_ALTERNATIVE_SELECTOR = "div[class*='notification']"
        
        try:
            while True:
                # 监听是否出现小红点（通知图标右上角）
                try:
                    program_logger.info("正在监听消息通知小红点...")
                    await page.wait_for_selector(NOTIFICATION_DOT_SELECTOR, timeout=60000)  # 等待1分钟
                    program_logger.info("检测到新消息小红点！")
                    
                    # 获取小红点的父元素（通知图标）
                    notification_dot = await page.query_selector(NOTIFICATION_DOT_SELECTOR)
                    if notification_dot:
                        try:
                            notification_icon = await notification_dot.evaluate_handle("(el) => el.parentElement")
                            if not notification_icon:
                                notification_icon = None
                        except:
                            notification_icon = None
                    else:
                        notification_icon = None
                    
                    # 如果无法通过小红点找到通知图标，尝试备选方案
                    if not notification_icon:
                        program_logger.warning("无法直接找到通知图标，尝试使用备选方案")
                        notification_icons = await page.query_selector_all(NOTIFICATION_ICON_ALTERNATIVE_SELECTOR)
                        if notification_icons:
                            notification_icon = notification_icons[0]
                        else:
                            program_logger.warning("无法找到通知图标，跳过此轮")
                            await page.wait_for_timeout(5000)
                            continue
                    
                    # 将鼠标悬停在通知图标上
                    try:
                        await notification_icon.hover()
                        program_logger.info("已将鼠标悬停在通知图标上")
                    except Exception as e:
                        program_logger.error(f"悬停失败: {str(e)}")
                        await page.wait_for_timeout(2000)
                        continue
                    
                    # 等待通知展开栏出现
                    try:
                        await page.wait_for_selector(NOTIFICATION_PANEL_SELECTOR, timeout=5000)
                        program_logger.info("通知展开栏已出现")
                        # 添加额外等待时间，确保展开栏内的DOM内容完全加载
                        await page.wait_for_timeout(2000)
                        
                        # 打印通知展开栏的DOM元素（debug级别）
                        try:
                            notification_panel = await page.query_selector(NOTIFICATION_PANEL_SELECTOR)
                            if notification_panel:
                                panel_html = await notification_panel.evaluate("el => el.outerHTML")
                                program_logger.debug(f"通知展开栏DOM结构: {panel_html}...")
                        except Exception as e:
                            program_logger.error(f"获取通知展开栏DOM失败: {str(e)}")
                    except Exception as e:
                        program_logger.warning(f"通知展开栏未出现: {str(e)}")
                        # 尝试使用更通用的选择器查找展开栏
                        panel_alternatives = await page.query_selector_all("div[class*='message-panel']")
                        if not panel_alternatives:
                            program_logger.warning("无法找到通知展开栏，跳过此轮")
                            await page.mouse.move(100, 100)
                            await page.wait_for_timeout(2000)
                            continue
                        else:
                            # 找到备选展开栏后也添加等待时间
                            program_logger.info("找到备选通知展开栏")
                            await page.wait_for_timeout(2000)                    
                    # 查找所有带有小红点的消息
                    message_dots = await page.query_selector_all(MESSAGE_DOT_SELECTOR)
                    if message_dots:
                        program_logger.info(f"找到 {len(message_dots)} 条未读消息")
                        
                        for i, dot in enumerate(message_dots):
                            program_logger.info(f"处理第{i+1}条消息...")
                            try:
                                # 先获取小红点元素的一些基本信息用于调试
                                dot_class = await dot.get_attribute("class")
                                dot_parent = await dot.evaluate_handle("(el) => el.parentElement")
                                parent_class = await dot_parent.get_attribute("class") if dot_parent else "未知父元素"
                                await dot_parent.dispose() if dot_parent else None
                                program_logger.debug(f"  小红点class: {dot_class}, 父元素class: {parent_class}")
                                 
                                # 尝试多种方式查找消息栏元素
                                # 方式1: 基于DOM结构的精确解析（推荐）
                                username = ""
                                message_content = ""
                                message_time = ""
                                user_label = ""
                                  
                                try:
                                    # 查找最近的消息容器元素
                                    message_container = await dot.evaluate_handle("(el) => el.closest('div.ScTolygy')")
                                    if message_container:
                                        # 尝试获取用户名 - 通过data-e2e属性
                                        username_element = await message_container.query_selector("div[data-e2e='user-name-card']")
                                        if username_element:
                                            username = await username_element.inner_text()
                                            program_logger.debug(f"  用户名: {username}")
                                        
                                        # 尝试获取用户标签
                                        label_element = await message_container.query_selector("div.OMk415Ng")
                                        if label_element:
                                            user_label = await label_element.inner_text()
                                            program_logger.debug(f"  用户标签: {user_label}")
                                        
                                        # 尝试获取评论内容
                                        comment_content_element = await message_container.query_selector("div.BBXP1E39.aZoh_6Ug pre")
                                        if comment_content_element:
                                            message_content = await comment_content_element.inner_text()
                                            program_logger.debug(f"  评论内容: {message_content}")
                                        
                                        # 尝试获取点赞或其他类型消息内容
                                        if not message_content:
                                            action_content_element = await message_container.query_selector("div.vfejYaR4.aZoh_6Ug")
                                            if action_content_element:
                                                message_content = await action_content_element.inner_text()
                                                program_logger.debug(f"  动作内容: {message_content}")
                                        
                                        # 尝试获取消息时间
                                        time_element = await message_container.query_selector("div.VmuA80Lm.uAgj2Uiz")
                                        if time_element:
                                            message_time = await time_element.inner_text()
                                            program_logger.debug(f"  消息时间: {message_time}")
                                        else:
                                            # 备选的时间选择器
                                            time_element_alt = await message_container.query_selector("div.RZzU8fyB.uAgj2Uiz span:last-child")
                                            if time_element_alt:
                                                message_time = await time_element_alt.inner_text()
                                                program_logger.debug(f"  消息时间(备选): {message_time}")
                                        
                                        # 如果获取了用户名，记录消息日志并通过QQ机器人发送
                                        if username:
                                            message_logger.info(f"用户名: {username}, 消息内容: {message_content}, 用户标签: {user_label}, 时间: {message_time}")
                                            # 通过QQ机器人发送消息
                                            try:
                                                qq_message = f"🎵 抖音新消息 🎵\n用户名: {username}\n{'' if not user_label else '标签: ' + user_label + '\n'}消息内容: {message_content}\n时间: {message_time}"
                                                api.send_private_text_sync(notify_qq, qq_message)
                                            except Exception as qq_err:
                                                program_logger.error(f"QQ机器人发送消息失败: {str(qq_err)}")
                                        
                                    # 释放handle
                                    await message_container.dispose()
                                except Exception as e:
                                    program_logger.error(f"  方式1(基于DOM解析)失败: {str(e)}")
                                 
                                # 方式2: 备选的DOM结构解析
                                if not username or (username and not message_content):
                                    try:
                                        # 使用稍微不同的方法查找消息容器
                                        message_container = await dot.evaluate_handle('(el) => {' +
                                            'let current = el;' +
                                            '// 向上查找3层父元素' +
                                            'for (let i = 0; i < 3; i++) {' +
                                            'if (!current || current.classList.contains(\'ScTolygy\')) break;' +
                                            'current = current.parentElement;' +
                                            '}' +
                                            'return current;}'
                                        )
                                        if message_container:
                                            # 尝试获取用户名
                                            username_element = await message_container.query_selector("div[data-e2e='user-name-card']")
                                            if username_element:
                                                username = await username_element.inner_text()
                                                program_logger.debug(f"  用户名(方式2): {username}")
                                        
                                            # 尝试获取用户标签
                                            label_element = await message_container.query_selector("div.OMk415Ng")
                                            if label_element:
                                                user_label = await label_element.inner_text()
                                                program_logger.debug(f"  用户标签(方式2): {user_label}")
                                        
                                            # 尝试获取评论内容
                                            comment_content_element = await message_container.query_selector("div.BBXP1E39.aZoh_6Ug pre")
                                            if comment_content_element:
                                                message_content = await comment_content_element.inner_text()
                                                program_logger.debug(f"  评论内容(方式2): {message_content}")
                                        
                                            # 尝试获取点赞或其他类型消息内容
                                            if not message_content:
                                                action_content_element = await message_container.query_selector("div.vfejYaR4.aZoh_6Ug")
                                                if action_content_element:
                                                    message_content = await action_content_element.inner_text()
                                                    program_logger.debug(f"  动作内容(方式2): {message_content}")
                                        
                                            # 尝试获取消息时间
                                            time_element = await message_container.query_selector("div.VmuA80Lm.uAgj2Uiz")
                                            if time_element:
                                                message_time = await time_element.inner_text()
                                                program_logger.debug(f"  消息时间(方式2): {message_time}")
                                            else:
                                                # 备选的时间选择器
                                                time_element_alt = await message_container.query_selector("div.RZzU8fyB.uAgj2Uiz span:last-child")
                                                if time_element_alt:
                                                    message_time = await time_element_alt.inner_text()
                                                    program_logger.debug(f"  消息时间(方式2备选): {message_time}")
                                        
                                            # 如果获取了用户名，记录消息日志并通过QQ机器人发送
                                            if username:
                                                message_logger.info(f"用户名: {username}, 消息内容: {message_content}, 用户标签: {user_label}, 时间: {message_time}")
                                                # 通过QQ机器人发送消息
                                                try:
                                                    qq_message = f"🎵 抖音新消息 🎵\n用户名: {username}\n{'' if not user_label else '标签: ' + user_label + '\n'}消息内容: {message_content}\n时间: {message_time}"
                                                    api.send_private_text_sync(notify_qq, qq_message)
                                                except Exception as qq_err:
                                                    program_logger.error(f"QQ机器人发送消息失败: {str(qq_err)}")
                                        
                                        # 释放handle
                                        await message_container.dispose()
                                    except Exception as e:
                                        program_logger.error(f"  方式2(基于DOM解析)失败: {str(e)}")
                                 
                                # 方式3: 直接获取整个通知面板的内容（作为最终备选）
                                if not username or (username and not message_content):
                                    try:
                                        # 查找整个通知面板
                                        notification_panel = await page.query_selector(NOTIFICATION_PANEL_SELECTOR)
                                        if notification_panel:
                                            # 尝试直接在面板中查找点赞消息和关注消息元素
                                            action_elements = await notification_panel.query_selector_all(":scope div.VQLtvMhG.aZoh_6Ug, :scope div.vfejYaR4.aZoh_6Ug")
                                            found_action_message = False
                                            
                                            for action_element in action_elements:
                                                action_text = await action_element.inner_text()
                                                if '赞了你的作品' in action_text or '关注了你' in action_text:
                                                    # 获取该消息的父容器
                                                    action_container = await action_element.evaluate_handle("(el) => el.closest('div.ScTolygy')")
                                                    if action_container:
                                                        # 尝试获取用户名
                                                        action_username_element = await action_container.query_selector("div[data-e2e='user-name-card']")
                                                        if action_username_element:
                                                            username = await action_username_element.inner_text()
                                                            message_content = action_text
                                                       
                                                        # 尝试获取用户标签
                                                        action_label_element = await action_container.query_selector("div.OMk415Ng")
                                                        if action_label_element:
                                                            user_label = await action_label_element.inner_text()
                                                     
                                                        # 尝试获取时间
                                                        action_time_element = await action_container.query_selector("div.VmuA80Lm.uAgj2Uiz")
                                                        if action_time_element:
                                                            message_time = await action_time_element.inner_text()
                                                        else:
                                                            # 备选的时间选择器
                                                            action_time_element_alt = await action_container.query_selector("div.XRmd3NFW.uAgj2Uiz")
                                                            if action_time_element_alt:
                                                                message_time = await action_time_element_alt.inner_text()
                                                     
                                                        program_logger.info(f"  检测到'{action_text}'消息 (方式3)")
                                                        found_action_message = True
                                                     
                                                        await action_container.dispose()
                                                        break
                                            
                                            # 如果找到动作消息(点赞或关注)，记录日志并通过QQ机器人发送
                                            if found_action_message and username:
                                                message_logger.info(f"用户名: {username}, 消息内容: {message_content}, 用户标签: {user_label}, 时间: {message_time}")
                                                # 通过QQ机器人发送消息
                                                try:
                                                    qq_message = f"🎵 抖音新消息 🎵\n用户名: {username}\n{'' if not user_label else '标签: ' + user_label + '\n'}消息内容: {message_content}\n时间: {message_time}"
                                                    api.send_private_text_sync(notify_qq, qq_message)
                                                except Exception as qq_err:
                                                    program_logger.error(f"QQ机器人发送消息失败: {str(qq_err)}")
                                            
                                    except Exception as e:
                                        program_logger.error(f"  方式3(基于DOM解析)失败: {str(e)}")
                                 
                                # 如果所有方式都失败，提供更简单的调试信息
                                if not message_content:
                                    program_logger.warning("  无法提取详细消息内容")
                                    # 尝试一种非常简单的方法 - 直接在控制台显示已检测到赞消息
                                    program_logger.info("  ✅ 已检测到新的点赞消息！")
                            except Exception as e:
                                program_logger.error(f"  处理消息时出错: {str(e)}")
                            
                            # 将鼠标从通知图标上移开
                            await page.mouse.move(100, 100)  # 移动到页面左上角
                            program_logger.info("已将鼠标从通知图标上移开")
                            
                            # 短暂暂停，避免处理过快
                            await page.wait_for_timeout(500)
                    else:
                        program_logger.info("未找到带有小红点的消息")
                        
                        # 将鼠标从通知图标上移开
                        await page.mouse.move(100, 100)  # 移动到页面左上角
                        program_logger.info("已将鼠标从通知图标上移开")
                        
                        # 短暂等待后继续监听
                        await page.wait_for_timeout(2000)
                    
                except Exception as e:
                    # 超时异常，继续监听
                    if "Timeout" in str(e):
                        program_logger.info("等待小红点超时，继续监听...")
                    else:
                        program_logger.error(f"发生错误: {str(e)}")
                    
                # 继续循环监听
                await page.wait_for_timeout(1000)
                
        except KeyboardInterrupt:
                program_logger.info("程序已停止")
        finally:
            # 关闭浏览器
            await browser.close()

if __name__ == "__main__":
    # 检查是否已安装playwright，如果没有则安装
    try:
        import playwright
    except ImportError:
        program_logger.info("未检测到playwright，正在安装...")
        import subprocess
        subprocess.run(["pip", "install", "playwright"], check=True)
        program_logger.info("正在安装浏览器...")
        subprocess.run(["playwright", "install"], check=True)
    
    # 运行主程序
    asyncio.run(main())